package com.freedom.sql.jdbc.pool.thread;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import com.freedom.sql.jdbc.statemachine.*;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
import com.alibaba.druid.sql.ast.statement.SQLShowTablesStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSetNamesStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowCreateTableStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowDatabasesStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowVariantsStatement;
import com.freedom.sql.jdbc.netty.MyNioSocketChannel;
import com.freedom.sql.jdbc.request.LoginPacket;
import com.freedom.sql.jdbc.request.SetNamesPacket;
import com.freedom.sql.jdbc.request.ShowCreateTable;
import com.freedom.sql.jdbc.request.ShowDatabasesPacket;
import com.freedom.sql.jdbc.request.ShowFullTablesPacket;
import com.freedom.sql.jdbc.request.UseDatabasePacket;
import com.freedom.sql.jdbc.response.ErrorPacket;
import com.freedom.sql.jdbc.response.GreetingPacket;
import com.freedom.sql.jdbc.utils.ByteUtils;
import com.freedom.sql.jdbc.utils.ErrorCodeUtils;
import com.freedom.sql.jdbc.utils.LoggerUtils;
import com.freedom.sql.jdbc.utils.MyException;
import com.freedom.sql.jdbc.utils.MySQLPacket;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;

public class MyRunnable implements Runnable {
	private static final Logger logger = LogManager.getLogger(MyRunnable.class);
	private ChannelHandlerContext context = null;
	private MyNioSocketChannel channel = null;
	private ByteBuf buffer = null;
	private int packetSeq = 0;
	private String content;

	// 根据bytes来决定是哪个状态
	public MyRunnable(ChannelHandlerContext _context, MyNioSocketChannel _channel, Object _obj) {
		context = _context;
		channel = _channel;
		buffer = (ByteBuf) _obj;
	}

	@Override
	public void run() {
		// 验证是否异步,经过验证,确实异步
		// if (null != context) {
		// LoggerUtils.info(logger,
		// "context.executor()---"+context.executor().getClass());
		// LoggerUtils.info(logger,
		// "context.executor().inEventLoop()---"+context.executor().inEventLoop());
		// }
		try {
			run0();
		} catch (Exception e) {
			LoggerUtils.error(logger, e.toString());
			// 看看是否可以获取主动关闭连接的标志
			boolean close = true;// 默认关闭
			if (e instanceof MyException) {
				close = ((MyException) e).getClose();
			}
			if (null != context) {
				new ErrorPacket(close, channel, packetSeq + 1, ErrorCodeUtils.ER_UNKNOWN_COM_ERROR, e.getMessage(),
						"28000").write();
			}
		}
	}

	private void run0() throws Exception {
		// 发送greeting报文
		if (null == buffer) {
			new GreetingPacket(channel, buffer).write();
			return;
		}
		// login packet
		if (false == channel.isLogined()) {// 当前没有登录，那么此次就是登录报文
			new LoginPacket(channel, buffer).read();
			return;
		}
		//
		// 这里就是接收各种命令了,各显神通
		// 先从buf里提取string的内容-//不支持数组方式
		LoggerUtils.debug(logger, "-------------------------------------");
		buffer.skipBytes(3);
		packetSeq = (byte) ByteUtils.readUnsignedByte(buffer);
		LoggerUtils.debug(logger, "packet seq ---" + packetSeq);
		byte type = (byte) ByteUtils.readUnsignedByte(buffer);
		LoggerUtils.debug(logger, "type---" + type);
		{
			int length = buffer.readableBytes();
			byte[] bytes = new byte[length];
			buffer.getBytes(5, bytes);
			if (null != channel.getCharset()) {
				content = new String(bytes, channel.getCharset());
			} else {
				content = new String(bytes);
			}
			LoggerUtils.info(logger,
					"all-sql is here[" + new String(bytes, channel.getCharset()) + "]" + buffer.toString());
		}
		//
		//
		//
		// 大分支
		switch (type) {
		case MySQLPacket.COM_QUIT:
			LoggerUtils.debug(logger, "it is a quit sql");
			quit();
			break;
		case MySQLPacket.COM_INIT_DB:
			// 比如use database xxx
			LoggerUtils.debug(logger, "it is a init db sql");
			initDB(content);// 跳转到initDB
			break;
		case MySQLPacket.COM_QUERY:
			// 查询
			LoggerUtils.debug(logger, "it is a query type sql");
			query(content);// 跳转到query语句
			break;
		case MySQLPacket.COM_PING:
		case MySQLPacket.COM_PROCESS_KILL:
		case MySQLPacket.COM_STMT_PREPARE:
		case MySQLPacket.COM_STMT_EXECUTE:
		case MySQLPacket.COM_STMT_CLOSE:
		case MySQLPacket.COM_HEARTBEAT:
		default:
			throw new MyException("Unknown By OurJDBC: " + new String(content), false);
		}
		// 结束
	}

	private void query(String content) throws Exception {
		// 根据小分支->具体的某一个xxxPacket分支
		SQLStatement statement = StateMachine.parseStatement(content);
		LoggerUtils.debug(logger, "" + statement.getClass());
		if (statement instanceof MySqlSetNamesStatement) {
			LoggerUtils.debug(logger, "[set names] type sql");
			new SetNamesPacket(packetSeq, channel, (MySqlSetNamesStatement) statement).read();
		} else if (statement instanceof MySqlShowDatabasesStatement) {
			LoggerUtils.debug(logger, "[show databases] type sql");
			new ShowDatabasesPacket(packetSeq, channel, (MySqlShowDatabasesStatement) statement).read();
		} else if (statement instanceof MySqlShowVariantsStatement) {
			// 本版本不支持
			LoggerUtils.debug(logger, "[show variables like] type sql");
			throw new MyException("Not Supported By OurJDBC: " + new String(content), false);
		} else if (statement instanceof SQLShowTablesStatement) {
			LoggerUtils.debug(logger, "[SHOW FULL TABLES WHERE Table_type !='XXX'] type sql");
			new ShowFullTablesPacket(packetSeq, channel, (SQLShowTablesStatement) statement).read();
		} else if (statement instanceof SQLSelectStatement) {
			// http://www.cnblogs.com/digdeep/p/5071204.html---讲解druid的功能
			//
			//
			//
			SQLSelectStatement concreteStatement = (SQLSelectStatement) statement;
			LoggerUtils.debug(logger, concreteStatement.getSelect().getClass().toString());
			throw new MyException("Not Supported By OurJDBC: " + new String(content), false);
			//
			//
			//
		} else if (statement instanceof MySqlShowCreateTableStatement) {
			LoggerUtils.debug(logger, "[SHOW Create Table] type sql");
			new ShowCreateTable(packetSeq, channel, (MySqlShowCreateTableStatement) statement, content).read();
		} else {
			throw new MyException("Unknown By OurJDBC: " + new String(content), false);
		}

		// 结束
	}

	// initdb
	private void initDB(String content) throws Exception {
		// 根据小分支->具体的某一个xxxPacket分支
		// State state = State.USE_DATABASE;
		new UseDatabasePacket(packetSeq, channel, content).read();
	}

	// quit
	private void quit() throws Exception {
		context.close();// 支持异步
		LoggerUtils.debug(logger, "socket is closed by client command");
	}

}
